Part One: The Scene Graph


Part Two: The VRML Source Code (moveball.wrl)

Here is the entire VRML 2.0 source code. Type (or paste) this code into a file called "moveball.wrl".

#VRML V2.0 utf8

PROTO Sony_BindSharedNode
  [ field SFNode transformNode NULL
    field SFNode scriptNode NULL ] {}

WorldInfo {
  title "shared ball"
  info ["VsServer:spiw.com:7009"] }

# the object which will be shared
DEF OBJ1 Transform {
  translation 0 0 0
  children [
    DEF HITME TouchSensor {}
	Shape {
	  geometry Sphere {}
      }
  ]  
}

#script for shared object
DEF ScriptObj1 Script {
  field SFNode obj1 USE OBJ1
  eventIn SFTime move
  eventIn SFString rpc_place_ball
  eventOut SFVec3f obj1position
  eventOut SFBool timerEnable
  url "moveball.class"
}

DEF CLOCK TimeSensor {
  loop TRUE
  enabled TRUE
  cycleInterval 0.2
  stopTime -1
}	
	
ROUTE ScriptObj1.obj1position TO OBJ1.set_translation
ROUTE ScriptObj1.timerEnable TO CLOCK.set_enabled
ROUTE CLOCK.cycleTime TO ScriptObj1.move

Sony_BindSharedNode {
  transformNode USE OBJ1
  scriptNode USE ScriptObj1
}

Part Three: The Java Source Code (moveball.java)

Here is the Java source code. Type this into a file called "moveball.java".

/*
  This Java file is an example of synchronized multi-user
  behaviour. The behaviour is of a ball moving in a loop.
  In all client worlds the ball is synchronized.

  Written By : Jai Natarajan
               Sony Pictures Imageworks
*/

import vrml.*;
import vs.*;
import java.lang.*;
import java.util.*;
import java.io.*;

public class moveball extends Script {
  float [] currPos = {0,0,0};

  SFNode obj1 = (SFNode)getField("obj1");
  Transform objT = (Transform)obj1.getValue();
  SFVec3f obj1position = (SFVec3f)getEventOut("obj1position");

  SFBool timerOn = (SFBool)getEventOut("timerEnable");
  
  public void moveball () { }

  // move : called by the timer every tick
  // ignored by everyone except the master, who updates
  // position and sends it to the others
  public void move(ConstSFTime ev, ConstSFTime time) {
    int j;

    if(Vscp.amIMaster()) {
      for(j = 0; j < 3; j++) {
        currPos[j] += 0.2;
        currPos[j] = currPos[j] % 5;
      } 
      String s = createStringFromPos(currPos);
      Vscp.sendApplSpecificMsgWithDist(obj1, "rpc_place_ball",
                                       s,Vscp.allClientsExceptMe);
      place_ball();
    }
  }
 
  // createStringFromPos : takes a position triplet and encodes
  // as a String to be passed as parameters for the rpc call
  // (shown in long and painful detail)
  public String createStringFromPos(float [] pos) {
    Float f0 = new Float(pos[0]);
    Float f1 = new Float(pos[1]);
    Float f2 = new Float(pos[2]);
    String delim = new String(" "); // delimiter
    String s00 = f0.toString();
    String s0 = s00.concat(delim);
    String s11 = f1.toString();
    String s1 = s11.concat(delim);
    String s22 = f2.toString();
    String s2 = s22.concat(delim); 
    String s01 = s0.concat(s1);
    String ans = s01.concat(s2);
    return(ans);
  }

  // getPosFromString : retrieves position triplet from String
  public float [] getPosFromString(String s) {
    float [] ans = new float[3];
    String delim = new String(" ");
    StringTokenizer st = new StringTokenizer(s, delim);
    String s0 = st.nextToken();
    String s1 = st.nextToken();
    String s2 = st.nextToken();

    Float f0 = new Float (s0);		
    Float f1 = new Float (s1);		
    Float f2 = new Float (s2);		

    ans[0] = f0.floatValue();
    ans[1] = f1.floatValue();
    ans[2] = f2.floatValue();
       
    return(ans);
  }

  // place_ball : Used by Master to put the ball in its new position
  public void place_ball () {
    obj1position.setValue(currPos);
  }

  // rpc_place_ball : called in clients (non-master) to positon ball
  public void rpc_place_ball (ConstSFString data, ConstSFTime time) {
    currPos = getPosFromString(data.getValue());
    place_ball();
  }
}